/*
   Copyright 2009 Abdulla Abdurakhmanov (abdulla@latestbit.com)
   Original sources are available at www.latestbit.com

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/

package org.latestbit.smartobjects.network.tests;

import static org.junit.Assert.*;

import java.net.URI;
import java.nio.ByteBuffer;

import org.junit.Test;
import org.latestbit.smartobjects.ISmartBus;
import org.latestbit.smartobjects.SmartBus;
import org.latestbit.smartobjects.network.*;
import org.latestbit.smartobjects.network.tcp.tests.*;


public class CoordinatorsTest {

	@Test
	public void testFailOverCoordinator() throws Exception {			
		ISmartBus bus = new SmartBus();
		// Creating servers
		URI server1URI = new URI("tcp://localhost:3333");
		TCPTestServer server1 = new TCPTestServer(server1URI);

		URI server2URI = new URI("tcp://localhost:3334");
		TCPTestServer server2 = new TCPTestServer(server2URI);
		
		
		server1.getAcceptor().start();		
		server2.getAcceptor().start();
		
		// Creating client
		ManagedNetwork network = new ManagedNetwork();
		IManagedClient client = network.createClient();
		IEndpointLocation serversLocation = new EndpointLocation(bus, server1URI, server2URI);
		IEndpointCoordinator coordinator = bus.getCoordinatorFactory().createCoordinator( serversLocation.getCoordinator() );
		
		client.setEndpointLocation( serversLocation, coordinator );
		ManagedConnectionListenerTester mcListener = new ManagedConnectionListenerTester();
		client.addConnectionListener(mcListener);

		// Check if the connection is available when servers is ready
		mcListener.getConditionLock().lock();
		server1.getListener().getConditionLock().lock();
		server2.getListener().getConditionLock().lock();
		
		client.start();
		mcListener.waitCondition();
		server1.getListener().waitCondition();
		server2.getListener().waitCondition();
		
		server2.getListener().getConditionLock().unlock();
		server1.getListener().getConditionLock().unlock();		
		mcListener.getConditionLock().unlock();
		
		assertTrue ( mcListener.isConnected() );

		// Stop one of the server and check the connection is still available
		mcListener.getConditionPerHandlerLock().lock();
		server1.getAcceptor().stop();
		mcListener.waitConditionPerHandler();
		mcListener.getConditionPerHandlerLock().unlock();		
		assertTrue ( mcListener.isConnected() );
		
		// Check that packet can be send to server2 when server1 is unavailable
		ConnectionListenerTester acceptorConnectionListener = new ConnectionListenerTester();
		server2.getListener().getAcceptedConnections().iterator().next().addStreamListener(acceptorConnectionListener);
		
		ByteBuffer buffer = ByteBuffer.allocate(5);
		buffer.put(new byte[] { 0x0A, 0x0B, 0x0C, 0x0D, 0x0E });
		buffer.flip();

		acceptorConnectionListener.getConditionLock().lock();
		client.getConnectionHandlerToSend().getConnection().send( buffer );
		acceptorConnectionListener.waitCondition();
		acceptorConnectionListener.getConditionLock().unlock();

		buffer.flip();
		assertTrue ( buffer.equals(acceptorConnectionListener.getReceivedBuffer())); 

		
		// Stop latest server and check the connection is unavailable
		mcListener.getConditionPerHandlerLock().lock();	
		mcListener.getConditionLock().lock();
		server2.getAcceptor().stop();
		mcListener.waitConditionPerHandler();
		mcListener.waitCondition();
		mcListener.getConditionLock().unlock();	
		mcListener.getConditionPerHandlerLock().unlock();
		assertFalse ( mcListener.isConnected() );		
		
		client.delConnectionListener(mcListener);
		client.stop();
		
		server1.getAcceptor().stop();
		server2.getAcceptor().stop();
	}
	
	@Test
	public void testRoundRobinCoordinator() throws Exception {		
		ISmartBus bus = new SmartBus();
		// Creating servers
		URI server1URI = new URI("tcp://localhost:3333");
		TCPTestServer server1 = new TCPTestServer(server1URI);

		URI server2URI = new URI("tcp://localhost:3334");
		TCPTestServer server2 = new TCPTestServer(server2URI);
		
		
		server1.getAcceptor().start();		
		server2.getAcceptor().start();
		
		// Creating client
		ManagedNetwork network = new ManagedNetwork();
		IManagedClient client = network.createClient();
		IEndpointLocation serversLocation = new EndpointLocation(bus, server1URI, server2URI);
		serversLocation.setCoordinator(StdRoundRobinCoordinator.COORDINATOR_NAME);
		IEndpointCoordinator coordinator = bus.getCoordinatorFactory().createCoordinator( serversLocation.getCoordinator() );
		
		client.setEndpointLocation( serversLocation, coordinator );
		ManagedConnectionListenerTester mcListener = new ManagedConnectionListenerTester();
		client.addConnectionListener(mcListener);

		// Check if the connection is available when servers is ready
		mcListener.getConditionPerHandlerLock().lock();
		server1.getListener().getConditionLock().lock();
		server2.getListener().getConditionLock().lock();
		
		client.start();
		server1.getListener().waitCondition();
		server2.getListener().waitCondition();
		mcListener.waitConditionPerHandler();
		if(mcListener.getLastHandlers().size() < 2 )
			mcListener.waitConditionPerHandler();
		
		server2.getListener().getConditionLock().unlock();
		server1.getListener().getConditionLock().unlock();		
		mcListener.getConditionPerHandlerLock().unlock();
		
		assertTrue ( mcListener.isConnected() );
		
		// Check that packet can be send to server1 and server2 by round-robin
		ConnectionListenerTester acceptorConnectionListener1 = new ConnectionListenerTester();
		server1.getListener().getAcceptedConnections().iterator().next().addStreamListener(acceptorConnectionListener1);

		ConnectionListenerTester acceptorConnectionListener2 = new ConnectionListenerTester();
		server2.getListener().getAcceptedConnections().iterator().next().addStreamListener(acceptorConnectionListener2);

		
		ByteBuffer buffer = ByteBuffer.allocate(5);
		buffer.put(new byte[] { 0x0A, 0x0B, 0x0C, 0x0D, 0x0E });		

		acceptorConnectionListener1.getConditionLock().lock();
		acceptorConnectionListener2.getConditionLock().lock();
		buffer.flip();
		client.getConnectionHandlerToSend().getConnection().send( buffer );
		buffer.flip();
		client.getConnectionHandlerToSend().getConnection().send( buffer );
		
		int sec=0;
		boolean result1 = false;
		boolean result2 = false;
		do {
			if(!result1)
				result1 = acceptorConnectionListener1.tryWaitCondition(1);
			if(!result2)
				result2 = acceptorConnectionListener2.tryWaitCondition(1);
			sec++;
		}
		while( !(result1 && result2) && sec < 5);
		acceptorConnectionListener2.getConditionLock().unlock();		
		acceptorConnectionListener1.getConditionLock().unlock();
		
		assertTrue (result1);
		assertTrue (result2);

		buffer.flip();
		assertTrue ( buffer.equals(acceptorConnectionListener1.getReceivedBuffer())); 
		assertTrue ( buffer.equals(acceptorConnectionListener2.getReceivedBuffer())); 		
		
		// Stop one of the server and check the connection is still available
		mcListener.getConditionPerHandlerLock().lock();
		server1.getAcceptor().stop();
		mcListener.waitConditionPerHandler();
		mcListener.getConditionPerHandlerLock().unlock();		
		assertTrue ( mcListener.isConnected() );
		
		// Check that packet can be send to server2 when server1 is unavailable
		acceptorConnectionListener2.setReceivedBuffer(null);
		acceptorConnectionListener2.getConditionLock().lock();
		client.getConnectionHandlerToSend().getConnection().send( buffer );
		acceptorConnectionListener2.waitCondition();
		acceptorConnectionListener2.getConditionLock().unlock();

		buffer.flip();
		assertTrue ( buffer.equals(acceptorConnectionListener2.getReceivedBuffer()));
		
		// Stop latest server and check the connection is unavailable
		mcListener.getConditionPerHandlerLock().lock();	
		mcListener.getConditionLock().lock();
		server2.getAcceptor().stop();
		mcListener.waitConditionPerHandler();
		mcListener.waitCondition();
		mcListener.getConditionLock().unlock();	
		mcListener.getConditionPerHandlerLock().unlock();
		assertFalse ( mcListener.isConnected() );		
		
		client.delConnectionListener(mcListener);
		client.stop();
		client.destroy();
		network.destroy();
		
		server1.getAcceptor().stop();
		server2.getAcceptor().stop();
		server1.getAcceptor().destroy();
		server2.getAcceptor().destroy();
	}	
}
